package cn.lu.train.downloader;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cn.lu.train.util.WeakHandler;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class Downloader {

    private ExecutorService mExeService;
    private static Downloader downloader;

    private Downloader() {
        mExeService = Executors.newFixedThreadPool(3);
    }

    public static Downloader gen() {
        if (downloader == null) {
            downloader = new Downloader();
        }
        return downloader;
    }

    public Builder build() {
        return new Builder();
    }

    static class Task extends DownloadTask implements Runnable, Handler.Callback{
        private static final int START = 0;
        private static final int STOP = 1;
        private static final int PROGRESS = 2;
        private static final int COMPLETE = 3;
        private static final int FAILURE = 4;

        private boolean isStop = true;
        private long length;
        private long progress;

        private Param param;
        private WeakHandler handler;
        private WeakReference<ExecutorService> mExe;

        Task(Param param, ExecutorService es) {
            this.param = param;
            mExe = new WeakReference<>(es);
            handler = new WeakHandler(Looper.getMainLooper(),this);
        }

        @Override
        public void start() {
            if (isStop) {
                ExecutorService es = mExe.get();
                if (es != null) {
                    isStop = false;
                    es.execute(this);
                }
            }
        }

        @Override
        public void stop() {
            if (!isStop) {
                isStop = true;
                handler.removeCallbacksAndMessages(null);
            }
        }

        @Override
        public boolean handleMessage(Message msg) {
            if (param.listener != null) {
                switch (msg.what) {
                    case START:
                        param.listener.onStart(this);
                        break;
                    case PROGRESS:
                        param.listener.onProgress(length, progress);
                        handler.sendEmptyMessageDelayed(PROGRESS, 500);
                        break;
                    case STOP:
                        break;
                    case COMPLETE:
                        handler.removeMessages(PROGRESS);
                        param.listener.onProgress(length, progress);
                        param.listener.onComplete(param.path);
                        break;
                    case FAILURE:
                        param.listener.onFailure(param.e);
                        break;
                }
            }
            return true;
        }

        @Override
        public void run() {
            if (isStop) {
                return;
            }

            OkHttpClient client = new OkHttpClient();
            final Request request = new Request.Builder()
                    .get()
                    .url(param.url)
                    .build();

            try {
                handler.sendEmptyMessage(START);
                Call call = client.newCall(request);
                Response response = call.execute();
                ResponseBody body = response.body();
                if (response.isSuccessful() && body != null) {
                    InputStream is = body.byteStream();
                    File file = new File(param.path);
                    FileOutputStream fos = new FileOutputStream(file);
                    byte[] temp = new byte[102400];
                    int len = 0;
                    length = body.contentLength();
                    handler.sendEmptyMessage(PROGRESS);
                    while (!isStop && (len = is.read(temp)) != -1) {
                        fos.write(temp, 0, len);
                        progress += len;
                    }
                    fos.flush();
                    fos.close();
                    is.close();
                    handler.sendEmptyMessage(COMPLETE);

                    if (isStop) {
                        file.delete();
                    }
                }
            }catch (IOException e) {
                param.e = e;
                handler.sendEmptyMessage(FAILURE);
                e.printStackTrace();
            } finally {
                isStop = true;
            }
        }

        @Override
        public String getUrl() {
            return param.url;
        }

        @Override
        public String getSavePath() {
            return param.path;
        }

        public long getLength() {
            return length;
        }

        @Override
        public int getState() {
            if (isStop) {
                return STATE_DOWNLOAD_STOP;
            }
            return STATE_DOWNLOADING;
        }
    }

    public class Builder{
        private Param mParam;
        private DownloadTask task;

        private Builder() {
            this.mParam = new Param();
        }

        public Builder create(String url) {
            mParam.url = url;
            return this;
        }

        public Builder setPath(String filePath) {
            mParam.path = filePath;
            return this;
        }

        public Builder setDownloadListener(DownloadListener listener) {
            mParam.listener = listener;
            return this;
        }

        public DownloadTask buildTask() {
            verifyParam();
            if (task == null) {
                task = new Task(mParam, mExeService);
            }
            return task;
        }

        public void start() {
            verifyParam();
            if (task == null) {
                task = new Task(mParam, mExeService);
            }
            task.start();
        }

        private void verifyParam() {
            if (mParam.url == null) {
                throw new IllegalArgumentException("url is not null");
            }
            if (mParam.path == null) {
                throw new IllegalArgumentException("path is not null");
            }
        }
    }

    private class Param{
        public String url;
        public String path;
        public DownloadListener listener;
        public Exception e;
    }

}
